package com.onlyxiahui.framework.net.session;

import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.locks.ReentrantLock;

import com.onlyxiahui.framework.net.session.server.DataItem;
import com.onlyxiahui.framework.net.session.server.DataQueueHandlerThread;
import com.onlyxiahui.framework.net.session.server.DataSendHandler;
import com.onlyxiahui.framework.net.session.server.DataItem.Type;

/**
 *
 * Date 2018-12-30 13:52:28<br>
 * Description
 *
 * @author XiaHui<br>
 * @since 1.0.0
 */
public class SessionBox {

	private DataQueueHandlerThread dataQueue;
	private DataSendHandler dataSendHandler;
	private final Map<String, CopyOnWriteArraySet<SocketSession>> sessionMap = new ConcurrentHashMap<>();
	private final CopyOnWriteArraySet<SocketSession> sessionSet = new CopyOnWriteArraySet<>();
	private final ReentrantLock lock = new ReentrantLock();
	private boolean useQueue = true;
	private int threshold = 100000;

	public SessionBox() {
		this(false);
	}

	public SessionBox(boolean useQueue) {
		this(useQueue, 3);
	}

	public SessionBox(int count) {
		this(true, count);
	}

	public SessionBox(boolean useQueue, int count) {
		this.useQueue = useQueue;
		dataSendHandler = new DataSendHandler(this);
		if (useQueue) {
			initialize(count);
		}
	}

	private void initialize(int count) {
		dataQueue = new DataQueueHandlerThread(count, dataSendHandler::send);
	}

	/**
	 *
	 * Date 2018-12-30 13:27:49<br>
	 * Description 存放用户连接的session<br>
	 *
	 * @author XiaHui<br>
	 * @param socketSession
	 * @since 1.0.0
	 */
	public void put(SocketSession socketSession) {
		String key = socketSession.getKey();
		CopyOnWriteArraySet<SocketSession> set = getSessionSet(key);
		set.add(socketSession);
		sessionSet.add(socketSession);
	}

	private CopyOnWriteArraySet<SocketSession> getSessionSet(String key) {
		CopyOnWriteArraySet<SocketSession> set = sessionMap.get(key);
		if (null == set) {
			final ReentrantLock l = this.lock;
			l.lock();

			try {
				set = sessionMap.get(key);
				if (set == null) {
					set = new CopyOnWriteArraySet<>();
					sessionMap.put(key, set);
				}
			} finally {
				l.unlock();
			}
		}
		return set;
	}

	/**
	 *
	 * Date 2018-12-30 13:39:35<br>
	 * Description 移除已经下线或者异常的用连接session
	 *
	 * @author XiaHui<br>
	 * @param key
	 * @return
	 * @since 1.0.0
	 */
	public CopyOnWriteArraySet<SocketSession> remove(String key) {
		CopyOnWriteArraySet<SocketSession> set = null;
		if (null != key) {
			set = sessionMap.remove(key);
			if (null != set) {
				for (SocketSession session : set) {
					sessionSet.remove(session);
				}
			}
		}
		return set;
	}

	/**
	 *
	 * Date 2018-12-30 13:40:02<br>
	 * Description 移除session
	 *
	 * @author XiaHui<br>
	 * @param socketSession
	 * @since 1.0.0
	 */
	public void remove(SocketSession socketSession) {
		sessionSet.remove(socketSession);
		String key = socketSession.getKey();
		if (null != key) {
			CopyOnWriteArraySet<SocketSession> set = sessionMap.get(key);
			if (null != set) {
				set.remove(socketSession);
				if (set.isEmpty()) {
					sessionMap.remove(key);
				}
			}
		}
	}

	/**
	 *
	 * Date 2018-12-30 14:31:39<br>
	 * Description 判断用户是否还有连接
	 *
	 * @author XiaHui<br>
	 * @param key
	 * @return
	 * @since 1.0.0
	 */
	public boolean hasSession(String key) {
		CopyOnWriteArraySet<SocketSession> set = sessionMap.get(key);
		boolean has = (null != set && !set.isEmpty());
		return has;
	}

	public boolean isUseQueue() {
		return useQueue;
	}

	public int getThreshold() {
		return threshold;
	}

	public void setThreshold(int threshold) {
		this.threshold = threshold;
	}

	public int size() {
		return null == dataQueue ? 0 : dataQueue.size();
	}

	public Set<SocketSession> getSet(String key) {
		Set<SocketSession> set = new HashSet<>();
		CopyOnWriteArraySet<SocketSession> values = sessionMap.get(key);
		if (null != values) {
			set.addAll(values);
		}
		return set;
	}

	public Set<SocketSession> getAllSet() {
		Set<SocketSession> set = new HashSet<>();
		set.addAll(sessionSet);
		return set;
	}

	protected CopyOnWriteArraySet<SocketSession> get(String key) {
		return sessionMap.get(key);
	}

	public void push(DataItem dataItem) {
		if (useQueue && getThreshold() > size()) {
			dataQueue.push(dataItem);
		} else {
			dataSendHandler.send(dataItem);
		}
	}

	public void push(Object message) {
		push(new DataItem(Type.all, "", message));
	}

	public void push(String key, Object data) {
		push(new DataItem(Type.one, key, data));
	}

	public void push(List<String> keyList, Object data) {
		push(new DataItem(Type.list, keyList, data));
	}

	public void pushWithout(String key, Object data) {
		push(new DataItem(Type.without, key, data));
	}

	public void pushWithout(List<String> keyList, Object data) {
		push(new DataItem(Type.without, keyList, data));
	}
}
